{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Anaconda3\\lib\\site-packages\\gensim\\utils.py:1197: UserWarning: detected Windows; aliasing chunkize to chunkize_serial\n",
      "  warnings.warn(\"detected Windows; aliasing chunkize to chunkize_serial\")\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import random\n",
    "from tqdm import tqdm\n",
    "from gensim.models import Word2Vec \n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import warnings;\n",
    "warnings.filterwarnings('ignore')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>InvoiceNo</th>\n",
       "      <th>StockCode</th>\n",
       "      <th>Description</th>\n",
       "      <th>Quantity</th>\n",
       "      <th>InvoiceDate</th>\n",
       "      <th>UnitPrice</th>\n",
       "      <th>CustomerID</th>\n",
       "      <th>Country</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>536365</td>\n",
       "      <td>85123A</td>\n",
       "      <td>WHITE HANGING HEART T-LIGHT HOLDER</td>\n",
       "      <td>6</td>\n",
       "      <td>2010-12-01 08:26:00</td>\n",
       "      <td>2.55</td>\n",
       "      <td>17850.0</td>\n",
       "      <td>United Kingdom</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>536365</td>\n",
       "      <td>71053</td>\n",
       "      <td>WHITE METAL LANTERN</td>\n",
       "      <td>6</td>\n",
       "      <td>2010-12-01 08:26:00</td>\n",
       "      <td>3.39</td>\n",
       "      <td>17850.0</td>\n",
       "      <td>United Kingdom</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>536365</td>\n",
       "      <td>84406B</td>\n",
       "      <td>CREAM CUPID HEARTS COAT HANGER</td>\n",
       "      <td>8</td>\n",
       "      <td>2010-12-01 08:26:00</td>\n",
       "      <td>2.75</td>\n",
       "      <td>17850.0</td>\n",
       "      <td>United Kingdom</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>536365</td>\n",
       "      <td>84029G</td>\n",
       "      <td>KNITTED UNION FLAG HOT WATER BOTTLE</td>\n",
       "      <td>6</td>\n",
       "      <td>2010-12-01 08:26:00</td>\n",
       "      <td>3.39</td>\n",
       "      <td>17850.0</td>\n",
       "      <td>United Kingdom</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>536365</td>\n",
       "      <td>84029E</td>\n",
       "      <td>RED WOOLLY HOTTIE WHITE HEART.</td>\n",
       "      <td>6</td>\n",
       "      <td>2010-12-01 08:26:00</td>\n",
       "      <td>3.39</td>\n",
       "      <td>17850.0</td>\n",
       "      <td>United Kingdom</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  InvoiceNo StockCode                          Description  Quantity  \\\n",
       "0    536365    85123A   WHITE HANGING HEART T-LIGHT HOLDER         6   \n",
       "1    536365     71053                  WHITE METAL LANTERN         6   \n",
       "2    536365    84406B       CREAM CUPID HEARTS COAT HANGER         8   \n",
       "3    536365    84029G  KNITTED UNION FLAG HOT WATER BOTTLE         6   \n",
       "4    536365    84029E       RED WOOLLY HOTTIE WHITE HEART.         6   \n",
       "\n",
       "          InvoiceDate  UnitPrice  CustomerID         Country  \n",
       "0 2010-12-01 08:26:00       2.55     17850.0  United Kingdom  \n",
       "1 2010-12-01 08:26:00       3.39     17850.0  United Kingdom  \n",
       "2 2010-12-01 08:26:00       2.75     17850.0  United Kingdom  \n",
       "3 2010-12-01 08:26:00       3.39     17850.0  United Kingdom  \n",
       "4 2010-12-01 08:26:00       3.39     17850.0  United Kingdom  "
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#https://archive.ics.uci.edu/ml/machine-learning-databases/00352/\n",
    "df = pd.read_excel('Online Retail.xlsx')\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(541909, 8)"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "InvoiceNo           0\n",
       "StockCode           0\n",
       "Description      1454\n",
       "Quantity            0\n",
       "InvoiceDate         0\n",
       "UnitPrice           0\n",
       "CustomerID     135080\n",
       "Country             0\n",
       "dtype: int64"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 检查缺失值数据df.isnull().sum()\n",
    "df.isnull().sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 删除缺失值所在行df.dropna(inplace=True)\n",
    "df.dropna(inplace=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#让我们将StockCode转换为string数据类型:\n",
    "df['StockCode']= df['StockCode'].astype(str)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4372"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#统计数据集中消费者数量\n",
    "customers = df[\"CustomerID\"].unique().tolist()\n",
    "len(customers)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#在我们的数据集中有4,372个消费者,对于这些消费者，我们将提取他们的购买历史。换句话说，我们可以有4372个购买序列。\n",
    "#留出数据集的一小部分用于验证是一个很好的方法。因此，我将使用90%消费者的数据来创建word2vec embeddings。让我们开始分割数据。\n",
    "#打乱消费者\n",
    "random.shuffle(customers)\n",
    "# 提取90%的消费者\n",
    "customers_train = [customers[i] for i in range(round(0.9*len(customers)))]\n",
    "\n",
    "# 分为训练集和验证集\n",
    "train_df = df[df['CustomerID'].isin(customers_train)]\n",
    "validation_df = df[~df['CustomerID'].isin(customers_train)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|████████████████████████████████████████████████████████████████████████████| 3935/3935 [00:02<00:00, 1588.83it/s]\n"
     ]
    }
   ],
   "source": [
    "# 存储消费者的购买历史\n",
    "purchases_train = []\n",
    "purchases_train_dict = {}\n",
    "# 用商品代码填充列表\n",
    "for i in tqdm(customers_train):\n",
    "    #把同一个客户的所有商品码处理成一个list\n",
    "    temp = train_df[train_df[\"CustomerID\"] == i][\"StockCode\"].tolist()\n",
    "    purchases_train.append(temp)\n",
    "    purchases_train_dict[i] = temp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "16384.0\n",
      "['22844', '22258', '47590A', '47590B', '21643', '22318', '22617', '22138', '20828', '22969', '79000', '22178', '71053', '85040A', '20972', '22256', '22957', '22249', '22445', '22587', '22584', '22744', '22852', '22853', '22698', '22699', '22697', '22423', '23171', '23170', '23173', '21974', '22776']\n"
     ]
    }
   ],
   "source": [
    "#\n",
    "for key in purchases_train_dict:\n",
    "    print( key )\n",
    "    print(purchases_train_dict[key])\n",
    "    break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████████████████████████████████████████████████████████████████████████| 437/437 [00:00<00:00, 2121.36it/s]\n"
     ]
    }
   ],
   "source": [
    "# 存储消费者的购买历史\n",
    "purchases_val = []\n",
    "# 用商品代码填充列表\n",
    "for i in tqdm(validation_df['CustomerID'].unique()):\n",
    "    temp = validation_df[validation_df[\"CustomerID\"] == i][\"StockCode\"].tolist()\n",
    "    purchases_val.append(temp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3685283, 3721870)"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 为商品构建word2vec Embeddings,训练word2vec模型\n",
    "model = Word2Vec(window = 10, \n",
    "                 sg = 1, \n",
    "                 hs = 0,\n",
    "                 negative = 10, # for negative sampling\n",
    "                 alpha=0.03, \n",
    "                 min_alpha=0.0007,\n",
    "                seed = 14)\n",
    "model.build_vocab(purchases_train, progress_per=200)\n",
    "model.train(purchases_train, total_examples = model.corpus_count, epochs=10, report_delay=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#因为我们不打算进一步训练模型，所以我们在这里调用init_sims()。这将使模型的内存效率更高:\n",
    "model.init_sims(replace=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Word2Vec(vocab=3176, size=100, alpha=0.03)\n"
     ]
    }
   ],
   "source": [
    "#查看“model”的相关参数:\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(3176, 100)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 提取向量\n",
    "X = model[model.wv.vocab]\n",
    "X.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "collapsed": false,
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1559d2908>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from umap.umap_ import UMAP\n",
    "#使用UMAP算法将商品Embeddings的维数从100降到2，UMAP算法通常用于降维。\n",
    "cluster_embedding = UMAP(n_neighbors=30, min_dist=0.0,n_components=2, random_state=42).fit_transform(X)\n",
    "\n",
    "plt.figure(figsize=(10,9))\n",
    "plt.scatter(cluster_embedding[:, 0], cluster_embedding[:, 1], s=3, cmap='Spectral')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "372187\n",
      "3666\n"
     ]
    }
   ],
   "source": [
    "#首先创建一个商品id和商品描述的字典，以便轻松地将商品的描述映射到其id，反之亦然。\n",
    "products = train_df[[\"StockCode\", \"Description\"]]\n",
    "print(len(products))\n",
    "\n",
    "# 去重\n",
    "products.drop_duplicates(inplace=True, subset='StockCode', keep=\"last\")\n",
    "print(len(products))\n",
    "\n",
    "# 创建一个商品id和商品描述的字典\n",
    "products_dict = products.groupby('StockCode')['Description'].apply(list).to_dict()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['RED WOOLLY HOTTIE WHITE HEART.']"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 字典测试products_dict['84029E']\n",
    "products_dict['84029E']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#定义了下面的函数。将一个商品的向量(n)作为输入，返回前6个相似的商品:\n",
    "def similar_products(v, n = 6):\n",
    "    # 为输入向量提取最相似的商品\n",
    "    ms = model.similar_by_vector(v, topn= n+1)[1:]\n",
    "    # 提取相似产品的名称和相似度评分\n",
    "    new_ms = []\n",
    "    for j in ms:\n",
    "        pair = (products_dict[j[0]][0], j[1])\n",
    "        new_ms.append(pair)\n",
    "    return new_ms        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('SILVER M.O.P ORBIT DROP EARRINGS', 0.7528426647186279),\n",
       " ('AMBER DROP EARRINGS W LONG BEADS', 0.7452982068061829),\n",
       " ('PINK SWEETHEART BRACELET', 0.7256171703338623),\n",
       " ('DROP DIAMANTE EARRINGS PURPLE', 0.7184370160102844),\n",
       " ('BLUE MURANO TWIST BRACELET', 0.7141450643539429),\n",
       " ('SILVER LARIAT BLACK STONE EARRINGS', 0.7084358334541321)]"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "similar_products(model['90019A'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#根据历史购买所有物品向量生成加权平均向量，然后计算这个平均向量的相似物品\n",
    "def aggregate_vectors(products):\n",
    "    product_vec = []\n",
    "    for i in products:\n",
    "        try:\n",
    "            product_vec.append(model[i])\n",
    "        except KeyError:\n",
    "            continue\n",
    "    return np.mean(product_vec, axis=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "314"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(purchases_val[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(100,)"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "aggregate_vectors(purchases_val[0]).shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('PARTY BUNTING', 0.6701889038085938),\n",
       " ('JAM MAKING SET WITH JARS', 0.6581915616989136),\n",
       " ('CONDIMENT TRAY 4 BOWLS AND 4 SPOONS', 0.6517977118492126),\n",
       " ('ALARM CLOCK BAKELIKE RED ', 0.6450258493423462),\n",
       " ('SPOTTY BUNTING', 0.639930248260498),\n",
       " ('ROSES REGENCY TEACUP AND SAUCER ', 0.6370081305503845)]"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#根据用户的整个购买历史推荐了6款商品\n",
    "similar_products(aggregate_vectors(purchases_val[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[('HOT WATER BOTTLE TEA AND SYMPATHY', 0.6706283688545227),\n",
       " ('LOVE HOT WATER BOTTLE', 0.6663874387741089),\n",
       " ('SCOTTIE DOG HOT WATER BOTTLE', 0.6611403226852417),\n",
       " ('HOT WATER BOTTLE I AM SO POORLY', 0.6602486371994019),\n",
       " ('KNITTED UNION FLAG HOT WATER BOTTLE', 0.655508279800415),\n",
       " ('CHOCOLATE HOT WATER BOTTLE', 0.6538037061691284)]"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "similar_products(aggregate_vectors(purchases_val[0][-10:]))"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python [Anaconda3]",
   "language": "python",
   "name": "Python [Anaconda3]"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
